Skip to content

fix: correct Copilot and Antigravity plugin output + polymorphic target registry - #13

Merged
steve-calvert-glean merged 5 commits into
mainfrom
refactor/polymorphic-target-registry
Jul 26, 2026
Merged

fix: correct Copilot and Antigravity plugin output + polymorphic target registry#13
steve-calvert-glean merged 5 commits into
mainfrom
refactor/polymorphic-target-registry

Conversation

@steve-calvert-glean

Copy link
Copy Markdown
Contributor

Summary

Fixes real, confirmed discrepancies between what pluginpack emits for the Copilot and Antigravity targets and what those hosts actually require — found while researching an unrelated feature, then independently re-verified (including against real published plugins in the wild, not just docs) before landing here. Introduces PluginTargetDefinition, a per-target registry that replaces scattered per-target logic across components.ts/targets.ts/validate.ts (the "touches ~5 places" pattern CLAUDE.md already flagged) — this is the first two targets migrated onto it; cursor/claude/codex follow in separate PRs.

⚠️ Behavior change: consumers of the copilot and antigravity targets will see different output on their next pluginpack build. This is a correctness fix, not a new feature — the previous output likely wasn't recognized correctly by either host.

Copilot

  • Was emitting no per-plugin plugin.json at all. The real spec (CLI plugin reference) requires one: "All plugins consist of a plugin directory containing, at minimum, a manifest file named plugin.json." Without it, a plugin likely wasn't recognized once Copilot fetched it from the marketplace's source.
  • Doc-derived location was also wrong. Every docs.github.com example tree shows plugin.json at the plugin root — but two real, working, GitHub/Microsoft-authored plugins (github/copilot-advanced-security-plugin, microsoft/skills-for-fabric) both put it at .github/plugin/plugin.json instead. Caught by checking real examples rather than trusting the docs' illustrative trees alone. Now mirrored at both locations, matching how the marketplace manifest already dual-writes for the same reason.
  • Added a validation check that agent files are named NAME.agent.md (Copilot derives agent identity from the filename).

Antigravity

  • Hooks were emitted at hooks/hooks.json (a directory, like every other target) but Antigravity's real convention (confirmed on two docs pages and two real published plugins) is a single root-level hooks.json file. Hooks authored for this target were silently inert.
  • plugin.json included a version field, but Antigravity's actual JSON Schema is additionalProperties: false with only name (required) and description (optional) — a strict validator rejects the whole manifest for an extra key, not just ignores it.
  • Reversed one of my own earlier claims: an initial pass said Antigravity doesn't support agents/ at all — it does; that was wrong and is corrected with a citation rather than silently dropped.

Architecture

New src/targets/ registry: each target implements one PluginTargetDefinition (component defaults, manifest/entry/marketplace builders, hooks/MCP config paths, validation, install-snippet builder, dated citations to the docs each fact traces to) instead of the same facts being scattered across three files independently. adapters.ts bridges old and new during migration — a target resolves through the registry if migrated, else falls back to the legacy function, so the build stays green target-by-target. The emit engine now actually consults each target's hooksPath() when placing hooks rather than copying them verbatim from source — that's what makes the Antigravity fix structural rather than a one-off patch.

Test plan

  • Every fix has a dedicated test asserting the exact spec-derived fact (not just "still builds") — e.g. a test that fails if version ever reappears in Antigravity's manifest, a test that fails if Copilot's two plugin.json copies diverge
  • npm run check (format, lint, typecheck, tests, build, docs) green — 56/56 tests pass
  • Re-verified both fixes against real published plugins on GitHub, not just documentation, specifically to catch the class of error a docs-only derivation can miss (which it did once this session — this PR's Copilot manifest-location fix exists because of that check)
  • Rebased cleanly onto main post-feat: add opt-in update-check hook for claude and cursor targets #12 (update-check) with zero conflicts — verified the two efforts don't share any modified source files

Follow-ups (intentionally not in this PR)

  • Cursor, Claude, Codex migrations onto the same registry (separate PRs, in that order)
  • Delete adapters.ts / legacy targetDefaultComponents once all 5 targets are migrated
  • Install-snippet feature on top of the corrected registry
  • Codex's .app.json (registered MCP servers) — noted as a known gap, not fixed here
  • A CHANGELOG.md entry — left for whoever cuts the next release, since this repo's changelog looks hand-batched at release time rather than per-PR

🤖 Generated with Claude Code

steve-calvert-glean and others added 4 commits July 26, 2026 11:28
Introduces PluginTargetDefinition — one interface a target implements
once (default components, manifest/entry/marketplace builders, hooks
and MCP config paths, validation, install snippets, citations) instead
of the same target's facts being scattered across components.ts,
targets.ts, and validate.ts independently. adapters.ts now bridges old
and new: a target resolves through the registry if migrated, falling
back to the legacy per-target functions otherwise, so targets migrate
one at a time without breaking the build in between.

The registry starts empty; this commit is a no-op bridge (verified via
the full existing test suite passing unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot's emitted output previously carried no per-plugin plugin.json
at all — a comment in the old emitCopilot said "Copilot has no
per-plugin manifest," which was true of an earlier, thinner version of
the format. The current spec is explicit: "All plugins consist of a
plugin directory containing, at minimum, a manifest file named
plugin.json" (docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference).
Without it, a plugin likely isn't recognized at all once Copilot
fetches it from the marketplace's `source`.

Migrates the copilot target onto the new PluginTargetDefinition
registry (see the scaffolding commit) as part of landing this fix, and
adds a validation check that agent files are named NAME.agent.md per
docs.github.com/.../plugins-creating, since Copilot derives agent
identity from the filename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two confirmed, verified-twice bugs in the antigravity target:

- Hooks were emitted at hooks/hooks.json (a directory), but Antigravity's
  actual convention (confirmed on both docs.google/docs/ide/plugins and
  .../docs/cli/plugins) is a single root-level hooks.json file. Hooks
  authored for this target were silently inert — the host never looked
  where pluginpack put them.
- The plugin.json manifest included a "version" field, but Antigravity's
  actual JSON Schema is additionalProperties: false with only "name"
  (required) and "description" (optional) — a strict validator would
  reject the whole manifest for the extra key, not just ignore it.

The emit engine (src/targets/engine.ts) now relocates a source plugin's
hooks/hooks.json to wherever the target's own hooksPath() says hooks
belong, rather than copying it verbatim — this is the actual fix, and
it generalizes: any future target with a different hooks convention
gets it right by construction instead of by remembering to special-case
it. validateAntigravity also now rejects any manifest field outside
{name, description} as a regression guard, and no longer requires
description (the schema makes it optional).

Also reversed, with a citation: an earlier pass claimed Antigravity
doesn't support an agents/ component at all. It does (per
antigravity.google/docs/cli/plugins) — that was wrong and is corrected
here; no change to defaultComponents was needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every docs.github.com example tree shows plugin.json at the plugin
root, which is what the previous commit shipped. Before treating that
fix as done, checked it against real published examples rather than
trusting the docs' illustrative trees alone — two real, working plugins
(github/copilot-advanced-security-plugin, microsoft/skills-for-fabric)
both put plugin.json at .github/plugin/plugin.json instead. Root-only
would have repeated the same class of bug (a plugin the real tooling
doesn't find where pluginpack put it), just relocated.

Mirrors it at both locations now, matching how the marketplace manifest
already dual-writes to .claude-plugin/ and .github/plugin/ for the same
reason. Generalized manifestPath -> manifestPaths on
PluginTargetDefinition to support this (cheap now, before more targets
implement the single-path version).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@steve-calvert-glean steve-calvert-glean added bug Something isn't working enhancement New feature or request and removed enhancement New feature or request labels Jul 26, 2026
Two changes across every source file:

- Every exported function, type, and const now has a JSDoc block
  (/** ... */) rather than a bare // line comment, matching normal
  TypeScript convention for documenting public API surface. Applied
  codebase-wide, not just to this session's new files, since a
  partial conversion would leave the codebase in a more inconsistent
  state than before.
- Removed comments that narrated the investigation itself rather than
  documenting a durable fact about the code — citation trails
  ("confirmed via direct fetch of..."), references to internal
  planning artifacts ("Plan 1's fix"), and before/after narration
  ("an earlier pass claimed X — reversed here"). That content belongs
  in commit messages and PR descriptions, which is where it already
  was — duplicating it into source comments only means it goes stale
  the next time someone re-verifies the underlying fact and doesn't
  think to hunt down every comment that mentions it.

No behavior changes; every edit is comment-only. Full test suite
(56/56) passes unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@steve-calvert-glean
steve-calvert-glean merged commit 57bfb39 into main Jul 26, 2026
1 check passed
@steve-calvert-glean
steve-calvert-glean deleted the refactor/polymorphic-target-registry branch July 26, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant